<template>
    <div class="monaco-editor-vue2" :id="domId"></div>
</template>
<script>
import { setLocaleData } from 'monaco-editor-nls';
import zh_CN from 'monaco-editor-nls/locale/zh-hans';
setLocaleData(zh_CN);
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main'
export default {
    name: 'MonacoEditor',
    props: {
        diffEditor: { type: Boolean, default: false },
        width: { type: [String, Number], default: '100%' },
        height: { type: [String, Number], default: '100%' },
        original: String,
        value: String,
        language: { type: String, default: 'javascript' },
        theme: { type: String, default: 'vs' },
        options: {
            type: Object,
            default() {
                return {}
            },
        },
        domId: { type: String, default: 'mcode' }
    },
    data() {
        return {
            style: {
                width: null,
                height: null
            },
            editor: undefined
        }
    },
    // emits: ['editorWillMount', 'editorDidMount', 'change', 'update:value'],
    mounted() {
        this.initMonaco()
    },
    beforeDestroy() {
        this.editor && this.editor.dispose()
    },
    methods: {
        initMonaco() {
            this.$nextTick(() => {
                this.$emit('editorWillMount', monaco)
                const { value, language, theme, options } = this;
                this.editor = monaco.editor[
                    this.diffEditor ? 'createDiffEditor' : 'create'
                ](this.$el, {
                    value: value,
                    language: language,
                    theme: theme,
                    ...options,
                })
                this.diffEditor && this._setModel(this.value, this.original);

                // @event `change`
                const editor = this._getEditor()
                editor &&
                    editor.onDidChangeModelContent((event) => {
                        const value = editor.getValue();
                        if (this.value !== value) {
                            this.$emit('change', value, event)
                            this.$emit('update:value', value)
                        }
                    })
                this.$emit('editorDidMount', this.editor)
            })
        },
        _setModel(value, original) {
            const { language } = this;
            const originalModel = monaco.editor.createModel(original, language)
            const modifiedModel = monaco.editor.createModel(value, language)
            this.editor.setModel({
                original: originalModel,
                modified: modifiedModel,
            })
        },
        _setValue(value) {
            let editor = this._getEditor()
            if (editor) return editor.setValue(value)
        },
        _getValue() {
            let editor = this._getEditor()
            if (!editor) return ''
            return editor.getValue()
        },
        _getEditor() {
            if (!this.editor) return null
            return this.diffEditor ? this.editor.modifiedEditor : this.editor
        },
        _setOriginal() {
            const { original } = this.editor.getModel()
            original.setValue(this.original)
        },
    },
    watch: {
        options: {
            deep: true,
            handler(options) {
                this.editor.updateOptions(options)
            },
        },
        value() {
            this.value !== this._getValue() && this._setValue(this.value)
        },
        original() {
            this._setOriginal()
        },
        language() {
            if (!this.editor) return
            if (this.diffEditor) {
                const { original, modified } = this.editor.getModel()
                monaco.editor.setModelLanguage(original, this.language)
                monaco.editor.setModelLanguage(modified, this.language)
            } else
                monaco.editor.setModelLanguage(this.editor.getModel(), this.language)
        },
        theme() {
            monaco.editor.setTheme(this.theme)
        },
    },
}
</script>
  
<style scoped lang="less">
.monaco-editor-vue2 {
    width: 100%;
    height: 100%;
}
</style>